using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Sockets;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Text;
using System.IO;
using Universe.Remoting.Extensions;
using Universe.Remoting.Sinks;

namespace Universe.Remoting
{
    public class ClientRepository
    {
        public static readonly ClientRepository Instance = new ClientRepository();

        public IList<Locator> Locators = new List<Locator>();

        static ClientRepository()
        {
            IClientChannelSinkProvider td = new CustomClientSinkProvider(typeof(TransparentDeliveryClientSink));
            IClientChannelSinkProvider f = new BinaryClientFormatterSinkProvider(new Hashtable(), new ArrayList());
            CustomClientSinkProvider sp = new CustomClientSinkProvider(typeof(TrafficClientSink));
            td.Next = f;
            f.Next = sp;
            
            TcpClientChannel channel = new TcpClientChannel(
                "tcp client II",
                td
                );

            ChannelServices.RegisterChannel(channel, false);
        }


        private ClientRepository()
        {
        }

        public Contract GetService<Contract>()
        {
            
            foreach (Locator locator in Locators)
            {
                if (ReferenceEquals(Locator.InProcess, locator))
                {
                    List<ServerRepository.Item> published = RemotingPublisher.PublishedServies;
                    ServerRepository.Item item = published.Find(
                        delegate(ServerRepository.Item obj)
                            {
                                return typeof (Contract) == obj.Contract;
                            });

                    if (item == null)
                        return default(Contract);

                    else if (item.Kind == WellKnownObjectMode.Singleton)
                        return (Contract) item.Singleton;

                    else if (item.Kind == WellKnownObjectMode.SingleCall)
                    {
                        return (Contract) Activator.CreateInstance(item.Implementation);
                    }
                }
            }

            string remoteUri = RemotingPublisher.GetAbsolutePath(typeof (Contract));
            foreach (Locator locator in Locators)
            {
                object raw = null;
                bool isOK = false;
                try
                {
                    raw = Activator.GetObject(typeof(Contract), "tcp://" + locator.Host + ":" + locator.Port + "/" + remoteUri);
                    bool isTrue = raw.Equals(null);
                    isOK = true;
                }
                catch (SocketException ex)
                {
                    // Trace.WriteLine("Socket Exception" + Environment.NewLine + ex);
                }
                catch (RemotingException ex)
                {
                    // Trace.WriteLine("Socket Exception" + Environment.NewLine + ex);
                }

                if (raw != null && isOK)
                {
                    if (!(raw is Contract))
                        throw new InvalidCastException("Expected '" + typeof(Contract).FullName + "' instance at tcp://" + locator.Host + ":" + locator.Port);

                    Contract ret = (Contract) raw;
                    return ret;
                }

                
            }

            return default(Contract);
        }
    }
}